package com.jbp.utils.encryptUtils;

import org.apache.logging.log4j.util.Base64Util;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.util.Arrays;
import java.util.Base64;

/**
 * @Auther:
 * @Date: 2018/10/19 15:49
 * @Description:
 */
public class AESUtil {
    static Logger logger = LoggerFactory.getLogger(AESUtil.class);
    /**
     * 密钥
     */
    public final static String TOKEN_KEY = "AD42F6697B035B7580E4FEF93BE20BAD";
    public final static String API_KEY = "GDv3HyyChel0RBSvjPgQvSCfIq4M/6beGZARk/nMS/A=";
    private static String charset = "utf-8";
    // 偏移量
    private static int offset = 16;
    // 加密器类型:加密算法为AES,加密模式为CBC,补码方式为PKCS5Padding
    private static String transformation = "AES/CBC/PKCS5Padding";
    // 算法类型：用于指定生成AES的密钥
    private static String algorithm = "AES";


    /*public static String generateDesKey() throws Exception {
        return generateDesKey(256);
    }*/
    /**
     * 生成符合AES要求的密钥
     * @param length
     * @return
     * @throws Exception
     */
    /*public static String generateDesKey(int length) throws Exception {
        //实例化
        KeyGenerator kgen = null;
        kgen = KeyGenerator.getInstance("AES");
        //设置密钥长度
        kgen.init(length);
        //生成密钥
        SecretKey skey = kgen.generateKey();
        //返回密钥的二进制编码
        return Base64Util.encodeBase64String(skey.getEncoded());
    }*/

    /**
     * 加密
     *
     * @param content
     * @return
     */
    public static String encrypt(String content) {
        return encrypt(content, API_KEY);
    }

    /**
     * 解密
     *
     * @param content
     * @return
     */
    public static String decrypt(String content) {
        return decrypt(content, TOKEN_KEY);
    }

    /**
     * 加密
     *
     * @param content 需要加密的内容
     * @param key     加密密码
     * @return
     */
    public static String encrypt(String content, String key) {
        return encrypt(content,key.getBytes());
    }
    public static String encrypt(String content, byte[] key) {
        try {
            //构造密钥
            SecretKeySpec skey = new SecretKeySpec(key, algorithm);
            //创建初始向量iv用于指定密钥偏移量(可自行指定但必须为128位)，因为AES是分组加密，下一组的iv就用上一组加密的密文来充当
            IvParameterSpec iv = new IvParameterSpec(key, 0, offset);
            System.out.println("key:"+ Arrays.toString(skey.getEncoded()));
            System.out.println("iv:"+ Arrays.toString(iv.getIV()));
            System.out.println("ivStr:"+ new String(iv.getIV()));

            //创建AES加密器
            Cipher cipher = Cipher.getInstance(transformation);
            byte[] byteContent = content.getBytes(charset);
            //使用加密器的加密模式
            cipher.init(Cipher.ENCRYPT_MODE, skey, iv);
            // 加密
            byte[] result = cipher.doFinal(byteContent);
            System.out.println(Base64.getEncoder().encodeToString(result));
            //使用BASE64对加密后的二进制数组进行编码
            return Base64.getEncoder().encodeToString(result);
//            return Base64Util.encodeBase64String(result);
        } catch (Exception e) {
            logger.error("AES加密异常", e);
        }
        return null;
    }

    /**
     * AES（256）解密
     *
     * @param content 待解密内容
     * @param key     解密密钥
     * @return 解密之后
     * @throws Exception
     */
    public static String decrypt(String content, String key) {
        return decrypt(content,key.getBytes());
    }
    public static String decrypt(String content,  byte[] key) {
        try {

            SecretKeySpec skey = new SecretKeySpec(key, algorithm);
            IvParameterSpec iv = new IvParameterSpec(key, 0, offset);
            Cipher cipher = Cipher.getInstance(transformation);
            //解密时使用加密器的解密模式
            // 初始化
            cipher.init(Cipher.DECRYPT_MODE, skey, iv);
            logger.warn("AES解密前数据:{}", content);
            byte[] result = cipher.doFinal(Base64.getDecoder().decode(content));
            // 解密
            logger.warn("AES解密后数据:{}",  new String(result,"utf-8"));
            return new String(result, "utf-8");
        } catch (Exception e) {
            logger.error("AES解密异常:{}", e.getMessage());
        }
        return null;
    }


    public static String CHARSET_UTF = "utf-8";


    public static String encrypt(String data, String key, String iv) {
        return encrypt(data, key, iv, false);
    }


    /**
     * encrypt use AES/CBC/PKCS5Padding
     *
     * @param data
     * @param key
     * @param iv
     * @return
     */
    public static String encrypt(String data, String key, String iv, boolean urlEncoderFlag) {
        try {
            Cipher cipher = createCipher(key, iv, Cipher.ENCRYPT_MODE);
            if (urlEncoderFlag) {
                return URLEncoder.encode(Base64.getEncoder().encodeToString(cipher.doFinal(data.getBytes())), CHARSET_UTF);
            } else {
                return Base64.getEncoder().encodeToString(cipher.doFinal(data.getBytes("UTF-8")));
            }
        } catch (Exception e) {
            logger.error("openssl encrypt fail,key:{}", key);
            e.printStackTrace();
        }
        return null;
    }


    public static String decrypt(String data, String key, String iv) {
        return decrypt(data, key, iv, false);
    }


    /**
     * decrypt
     *
     * @param data
     * @param key
     * @param iv
     * @return
     */
    public static String decrypt(String data, String key, String iv, boolean urlDecoderFlag) {
        try {
            Cipher cipher = createCipher(key, iv, Cipher.DECRYPT_MODE);
            if (urlDecoderFlag) {
                return new String(cipher.doFinal(Base64.getDecoder().decode(URLDecoder.decode(data, CHARSET_UTF))));
            } else {
                return new String(cipher.doFinal(Base64.getDecoder().decode(data)));
            }
        } catch (Exception e) {
            logger.error("openssl decrypt fail,key:{}", key, e);
        }
        return null;
    }

    /**
     * create cipher
     *
     * @param key
     * @param iv
     * @param mode
     * @return
     * @throws Exception
     */
    private static Cipher createCipher(String key, String iv, int mode) throws Exception {
        byte[] keyBytes = key.getBytes();
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        IvParameterSpec ivParameterSpec = new IvParameterSpec(iv.getBytes());
        cipher.init(mode, new SecretKeySpec(keyBytes, "AES"), ivParameterSpec);
        return cipher;
    }

    public static void println(String encrypt){
//        System.out.println(decrypt(encrypt,Base64Util.decodeBase64(API_KEY)));
    }

    public static void main(String[] args) throws Exception {
        encrypt("xxxx");
    }
}
